package com.eunion.fileserver.ftp;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.eunion.fileserver.upload.FileUploadHelper;

/**
 * Ftp　上传下载等帮助类.
 * Usage:
 * <p>new FtpHelper(ftpIp, 21, ftpUser,ftpPwd).uploadFTPFile("d:/V0.2.png", "ftpName.xxx", "test/dir");
 * <p>new FtpHelper(ftpIp, 21, ftpUser,ftpPwd).uploadFTPFile(new FileInputStream(new File("d:/V0.2.png")), "ftpName2.xxx", "test/dir");
 * <p>new FtpHelper(ftpIp, 21, ftpUser,ftpPwd).uploadFTPFile(new FileInputStream(new File("d:/V0.2.png")), "ftpName2.xxx", "/");
 * <p>new FtpHelper(ftpIp, 21, ftpUser,ftpPwd).downloadFTPFile("ftpName2.xxx","G:/V0.2.png");//OK
 * <p>new FtpHelper(ftpIp, 21, ftpUser,ftpPwd).downloadFTPFile("ftpName2.xxx","G:/aaa/V0.2.png");//java.io.FileNotFoundException: G:\aaa\V0.2.png (系统找不到指定的路径。)
 * <p>new FtpHelper(ftpIp, 21, ftpUser,ftpPwd).downloadFTPFile("test/dir/ftpName2.xxx","G:/V0.3.png");//OK
 * <p>System.out.println(new FtpHelper(ftpIp, 21, ftpUser,ftpPwd).downloadFTPFile("test/dir/ftpName2.xxx"));
 * <p>System.out.println(Arrays.asList(new FtpHelper(ftpIp, 21, ftpUser,ftpPwd).listFTPFiles("test/dir")));
 * @version 2016年7月3日 上午8:07:00
 */
public class FtpHelper {

	private static Logger LOG = LoggerFactory.getLogger(FtpHelper.class);

	private FTPClient ftp = null;// Creating FTP Client instance

	private String ftpHost;

	private Integer ftpPort;

	private String ftpUser;

	private String ftpPwd;

	private boolean isConnect = Boolean.FALSE;//是否已经执行connnectFtp()

	/**
	 * 私有默认构造函数.
	 */
	private FtpHelper() {
		//		this.ftpHost = FUtils.getFtpIp();
		//		this.ftpPort = FUtils.getFtpPort();
		//		this.ftpUser = FUtils.getFtpUser();
		//		this.ftpPwd = FUtils.getFtpPwd();
	}

	/**
	 * 初始化ftp 基本数据.
	 * @param host 主机，一般为ip，不包含下级目录
	 * @param port 端口
	 * @param username 用户名
	 * @param password 密码
	 */
	public FtpHelper(String host, int port, String username, String password) {
		this();
		this.ftpHost = host;
		this.ftpPort = port;
		this.ftpUser = username;
		this.ftpPwd = password;
	}

	/**
	 * 创建ftp对象.
	 * @param host
	 * @param port
	 * @param username
	 * @param password
	 * @throws Exception
	 */
	public boolean connnectFtp() {
		try {
			this.ftp = new FTPClient();
			this.ftp.setControlEncoding(FileUploadHelper.CHAR_UTF8);
			//ftp.addProtocolCommandListener(new PrintCommandListener(new PrintWriter(System.out)));
			this.ftp.connect(ftpHost, ftpPort);
			LOG.debug("FTP connect executed");
			this.ftp.login(ftpUser, ftpPwd);
			int reply = ftp.getReplyCode();
			LOG.debug("FTP connect login reply code : {}", reply);
			if (!FTPReply.isPositiveCompletion(reply)) {
				LOG.error("FTP connect login reply error , now disconnect and return  : {}", reply);
				this.disconnect();
				return Boolean.FALSE;
			}
			this.ftp.setFileType(FTP.BINARY_FILE_TYPE);// 设置传输二进制文件 
			this.ftp.enterLocalPassiveMode();
			this.ftp.setFileTransferMode(FTP.STREAM_TRANSFER_MODE);
			LOG.debug("FTP connect login ok");
			isConnect = true;
			return Boolean.TRUE;
		} catch (Exception e) {
			LOG.error("FTP connect error", e);
		}
		return Boolean.FALSE;
	}

	/**
	 * 断开连接.关闭.
	 */
	private void disconnect() {
		try {
			if (null != this.ftp) {
				LOG.debug("FTP disconnect begin {}", this.ftp.isConnected());
				if (this.ftp.isConnected()) {
					try {
						this.ftp.logout();
						this.ftp.disconnect();
					} catch (IOException e) {
						LOG.error("FTP disconnect error", e);
					}
				}
			} else {
				LOG.debug("FTP  is null");
			}
		} finally {
			this.isConnect = Boolean.FALSE;
		}
	}

	/**
	 * uploadFile 上传文件.
	 * @param localFileFullPathName 本地文件绝对路径+文件名
	 * @param fileName  存储到远程的文件名.
	 * @param hostDir 存储到远程的目录
	 * @throws Exception
	 */
	private boolean uploadFTPFile(String localFileFullPathName, String fileName, String hostDir) {
		if (!isConnect) {
			boolean cFlag = this.connnectFtp();
			if (!cFlag) {
				return Boolean.FALSE;
			}
		} //connect ftp
		try {
			InputStream input = new FileInputStream(new File(localFileFullPathName));
			return uploadFTPFile(input, fileName, hostDir);
		} catch (Exception e) {
			throw new RuntimeException("FTP上传文件失败", e);
		} finally {
			this.disconnect();
		}
	}

	/**
	 * uploadFile 上传文件.
	 * @param input 本地文件流
	 * @param fileName  存储到远程的文件名.
	 * @param hostDir 存储到远程的目录
	 * @throws Exception
	 */
	public boolean uploadFTPFileFromInputStream(InputStream input, String fileName, String hostDir) {
		if (!isConnect) {
			boolean cFlag = this.connnectFtp();
			if (!cFlag) {
				return Boolean.FALSE;
			}
		} //connect ftp
		try {
			return uploadFTPFile(input, fileName, hostDir);
		} catch (Exception e) {
			throw new RuntimeException("FTP上传文件失败", e);
		} finally {
			this.disconnect();
		}
	}

	/**
	 * uploadFile 上传文件.finally中负责关闭文件流
	 * @param input 本地文件流
	 * @param fileName  存储到远程的文件名.
	 * @param hostDir 存储到远程的目录
	 * @throws Exception
	 */
	private boolean uploadFTPFile(InputStream input, String fileName, String hostDir)
	    throws Exception {
		if (!isConnect) {
			boolean cFlag = this.connnectFtp();
			if (!cFlag) {
				return Boolean.FALSE;
			}
		}
		try {
			boolean mkdirFlag = this.mkDir(hostDir);
			LOG.debug("FTP mkdir {} status : {}", hostDir, mkdirFlag);
			boolean changeDirFlag = this.changeDir(hostDir);
			LOG.debug("FTP changeDir {} status : {}", hostDir, changeDirFlag);
			boolean storeFlag = this.ftp.storeFile(fileName, input);
			LOG.debug("FTP upload file status : {}", storeFlag);
			return storeFlag;
		} catch (Exception e) {
			throw new RuntimeException("FTP上传文件失败", e);
		} finally {
			//			if (input != null) {
			//				input.close();//close input finally
			//			}
			this.disconnect();
		}
	}

	/**
	 * 逐级创建目录 .
	 * @param dirs  a  a/b a/b/
	 * @return
	 * @throws IOException
	 */
	private boolean mkDir(String dirs)
	    throws IOException {
		this.ftp.changeWorkingDirectory(FileUploadHelper.ROOT_PATH);
		if (StringUtils.isEmpty(dirs) || dirs.equals(FileUploadHelper.ROOT_PATH)) {
			return Boolean.TRUE;
		}
		dirs = dirs.trim();
		String dir[] = dirs.split(FileUploadHelper.ROOT_PATH);
		if (dir != null && dir.length > 0) {
			for (String d : dir) {
				if (StringUtils.isNotEmpty(d)) {
					boolean flag = this.ftp.makeDirectory(d);
					if (!flag) {
						if (this.ftp.changeWorkingDirectory(d)) {
							continue;//这个目录已经创建，不需要再建立了
						}
						LOG.error("create dir {} error ", d);
						return Boolean.FALSE;
					}
					this.ftp.changeWorkingDirectory(d);//切换进入
				}
			}
		}
		return true;
	}

	/**
	 * 逐级进入目录 .
	 * @param dirs  a  a/b a/b/
	 * @return
	 * @throws IOException
	 */
	private boolean changeDir(String dirs)
	    throws IOException {
		this.ftp.changeWorkingDirectory(FileUploadHelper.ROOT_PATH);
		if (StringUtils.isEmpty(dirs) || dirs.equals(FileUploadHelper.ROOT_PATH)) {
			this.ftp.changeWorkingDirectory(FileUploadHelper.ROOT_PATH);
			return Boolean.TRUE;
		}
		dirs = dirs.trim();
		String dir[] = dirs.split(FileUploadHelper.ROOT_PATH);
		if (dir != null && dir.length > 0) {
			for (String d : dir) {
				if (StringUtils.isNotEmpty(d)) {
					boolean flag = this.ftp.changeWorkingDirectory(d);
					if (!flag) {
						LOG.error("change dir {} error ", d);
						return Boolean.FALSE;
					}
				}
			}
		}
		return true;
	}

	/**
	 * 下载文件.
	 * @param source ftpPathName
	 * @param destination localPathName
	 */
	public boolean downloadFTPFile(String source, String destination) {
		if (!isConnect) {
			boolean cFlag = this.connnectFtp();
			if (!cFlag) {
				return Boolean.FALSE;
			}
		}
		FileOutputStream fos = null;
		try {
			String destinationFolder = destination.substring(0, destination.lastIndexOf(FileUploadHelper.ROOT_PATH));
			File folder = new File(destinationFolder);
			if (!folder.exists()) {
				boolean flag = folder.mkdirs();
				if (!flag) {
					LOG.error("create local folder for {} file error ", destination);
					return Boolean.FALSE;
				}
			}
			fos = new FileOutputStream(destination);
			Boolean flag = this.ftp.retrieveFile(source, fos);
			LOG.debug("download {} to {} status {}", source, destination, flag);
			return flag;
		} catch (Exception e) {
			LOG.error("Ftp download error ", e);
			return Boolean.FALSE;
		} finally {
			if (fos != null) {
				try {
					fos.close();
				} catch (IOException e) {
					LOG.error("Ftp close outpPutStream error ", e);
				} //close input finally
			}
			this.disconnect();
		}
	}

	/**
	 * 下载文件.
	 * @param source ftpPathName
	 * @param destination localPathName
	 */
	public InputStream downloadFTPFile(String source) {
		if (!isConnect) {
			boolean cFlag = this.connnectFtp();
			if (!cFlag) {
				return null;
			}
		}
		ByteArrayOutputStream fos = null;
		try {
			fos = new ByteArrayOutputStream();
			Boolean flag = this.ftp.retrieveFile(source, fos);
			ByteArrayInputStream swapStream = new ByteArrayInputStream(fos.toByteArray());
			LOG.debug("download {} tostatus {}", source, flag);
			return swapStream;
		} catch (Exception e) {
			LOG.error("Ftp download error ", e);
			return null;
		} finally {
			if (fos != null) {
				try {
					fos.close();
				} catch (IOException e) {
					LOG.error("Ftp close outpPutStream error ", e);
				} //close input finally
			}
			this.disconnect();
		}
	}

	/**
	 * 删除文件.
	 * @param source ftpPathName
	 */
	public boolean deleteFTPFile(String source) {
		if (!isConnect) {
			boolean cFlag = this.connnectFtp();
			if (!cFlag) {
				return false;
			}
		}
		try {
			return this.ftp.deleteFile(source);
		} catch (Exception e) {
			LOG.error("Ftp download error ", e);
			return false;
		} finally {
			this.disconnect();
		}
	}

	/**
	 * list the files in a specified directory on the FTP 
	 */
	public List<String> listFTPFiles(String directory) {
		try {
			if (!isConnect) {
				boolean cFlag = this.connnectFtp();
				if (!cFlag) {
					return new ArrayList<String>(0);
				}
			}
			// lists files and directories in the current working directory
			FTPFile[] files = this.ftp.listFiles(directory);
			List<String> fileNames = new ArrayList<String>();
			for (FTPFile file : files) {
				String details = file.getName();
				fileNames.add(details);
			}
			return fileNames;
		} catch (Exception e) {
			LOG.error("列出ftp文件列表失败.", e);
			return null;
		}
	}

	//测试
	public static void main(String[] args)
	    throws Exception {
		String ftpIp = "115.28.226.78";//ftpIp
		String ftpUser = "ftpadmin";//ftpUser
		String ftpPwd = "ftpadminPwd123";//"ken111"
		String ftpdir = "test/dir";
		//		int ftpPort = 21;
		//		new FtpHelper(ftpIp, 21, ftpUser,ftpPwd).uploadFTPFile("d:/V0.2.png", "ftpName.xxx", "test/dir");
		//		new FtpHelper(ftpIp, 21, ftpUser,ftpPwd).uploadFTPFile(new FileInputStream(new File("d:/V0.2.png")),"ftpName2.xxx", "test/dir");
		//		new FtpHelper(ftpIp, 21, ftpUser,ftpPwd).uploadFTPFile(new FileInputStream(new File("d:/V0.2.png")),"ftpName2.xxx", "/");
		//		new FtpHelper(ftpIp, 21, ftpUser,ftpPwd).downloadFTPFile("ftpName2.xxx", "G:/V0.2.png");//OK
		//		new FtpHelper(ftpIp, 21, ftpUser,ftpPwd).downloadFTPFile("ftpName2.xxx", "G:/aaa/V0.2.png");//java.io.FileNotFoundException: G:\aaa\V0.2.png (系统找不到指定的路径。)
		//		new FtpHelper(ftpIp, 21, ftpUser,ftpPwd).downloadFTPFile("test/dir/ftpName2.xxx", "G:/V0.3.png");
		//		System.out.println(new FtpHelper(ftpIp, 21, ftpUser,ftpPwd).downloadFTPFile("test/dir/ftpName2.xxx"));
		//		System.out.println(Arrays.asList(new FtpHelper(ftpIp, 21, ftpUser,ftpPwd).listFTPFiles("test/dir")));
		LOG.info("begin 1 " + new Date());
		boolean flag = new FtpHelper(ftpIp, 21, ftpUser, ftpPwd).uploadFTPFile("C:/Users/admin/Desktop/worth/60kb201282413335761587.jpg", "60kb201282413335761587.jpg", ftpdir);
		LOG.info(flag + " begin 2 " + new Date());
		boolean flag2 = new FtpHelper(ftpIp, 21, ftpUser, ftpPwd).uploadFTPFile("C:/Users/admin/Desktop/worth/6MB20170215095446.jpg", "60kb201282413335761587.jpg", ftpdir);
		LOG.info(flag2 + " begin 3 " + new Date());
		//		new FtpHelper(ftpIp, 21, ftpUser,ftpPwd).downloadFTPFile("test/dir/ftpName2.xxx", "G:/V0.3.png");
		//		System.out.println(new FtpHelper(ftpIp, 21, ftpUser,ftpPwd).downloadFTPFile("test/dir/ftpName2.xxx"));
		//		System.out.println(Arrays.asList(new FtpHelper(ftpIp, 21, ftpUser,ftpPwd).listFTPFiles(ftpdir)));
	}

}
